﻿/*
2014-07-17, Honda, Add debug info.

*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// add
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;
using LibTCP;



namespace TCPAsyncServer
{
    class Program
    {
        static Socket mServerSocket;
        static readonly List<Socket> mClientSockets = new List<Socket>();
        const int gcBUFFER_SIZE = 2048;
        const int gcPORT = 9000;
        static readonly byte[] _buffer = new byte[gcBUFFER_SIZE];
        
        // My global
        static int giLastError;
        static string gsLastError;
        static byte[] gbaMyData;
        static int giMyData;
        static string gsMydata;


        static void Main(string[] args)
        {
            Console.Title = "Server";
            if (SetupServer())
            {
                Console.ReadLine(); // When we press enter close everything
                CloseAllSockets();
            }
            else
            {
                Console.WriteLine("Press enter to close.");
                Console.ReadLine(); // When we press enter close everything
            }

        }
        private static bool SetupServer()
        {
            Console.WriteLine("Setting up server...");
            mServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
            // todo: port is opened.
            try
            {
                mServerSocket.Bind(new IPEndPoint(IPAddress.Any, gcPORT));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                //Debug.Assert(false, ex.Message);
                return false;
            }
            
            mServerSocket.Listen(5);
            mServerSocket.BeginAccept(AcceptCallback, null);
            Console.WriteLine("Server setup complete");
            return true;
        }

        /// <summary>
        /// Close all connected client (we do not need to shutdown the server socket as its connections
        /// are already closed with the clients)
        /// </summary>
        private static void CloseAllSockets()
        {
            foreach (Socket socket in mClientSockets)
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }

            mServerSocket.Close();
        }

        private static void AcceptCallback(IAsyncResult AR)
        {
            Socket socket;

            try
            {
                socket = mServerSocket.EndAccept(AR);
            }
            catch (ObjectDisposedException) // I cannot seem to avoid this (on exit when properly closing sockets)
            {
                return;
            }

            mClientSockets.Add(socket);
            socket.BeginReceive(_buffer, 0, gcBUFFER_SIZE, SocketFlags.None, ReceiveCallback, socket);
            Console.WriteLine("Client connected, waiting for request...");
            mServerSocket.BeginAccept(AcceptCallback, null);
        }

        public static byte XOR(byte b1, byte b2)
        {
            return Convert.ToByte(b1 ^ b2);
        }

        public static byte GetChecksumXOR(byte[] ba1)
        {
            byte byte1;

            byte1 = ba1[0];
            for (int i = 1; i < ba1.Length; i++)
                byte1 = XOR(byte1, ba1[i]);

            return byte1;
        }
        public static int CompareBytes(byte[] ba1, byte[] ba2)
        {
            return CompareBytes(ba1, ba2, 0, ba1.Length);
        }
        public static int CompareBytes(byte[] ba1, byte[] ba2, int iStart, int iLen)
        {
            for (int i = iStart; i < iLen; i++)
                if (ba1[i] < ba2[i])
                    return -1;
                else if (ba1[i] > ba2[i])
                    return 1;

            return 0;
        }
        public static string DumpBuffer(byte[] ba, int iStart, int iLen)
        {
            StringBuilder sb = new StringBuilder();

            if (iLen < 1)
                return string.Empty;
            if (ba == null)
                return string.Empty;

            sb.Length = 0;
            sb.AppendFormat("DumpBuffer(): length={0}.", iLen);
            sb.AppendLine();

            sb.AppendFormat("{0}", CCommon.ToHex(ba, iStart, iLen));
            sb.AppendLine(".");

            for (int i = 0; i < iLen; i++)
                sb.AppendFormat("{0,-3}", CCommon.ToASCIIPrintable(ba[i]));
            sb.AppendLine(".");

            for (int i = 0; i < iLen; i++)
                sb.AppendFormat("{0,-3}", i.ToString("X"));
            sb.AppendLine(".");
            for (int i = 0; i < iLen; i++)
                sb.AppendFormat("{0,-3}", i);
            sb.AppendLine(".");

            return sb.ToString();
        }




        private static void ReceiveCallback(IAsyncResult AR)
        {
            Socket current = (Socket)AR.AsyncState;
            int received;

            try
            {
                received = current.EndReceive(AR);
            }
            catch (SocketException)
            {
                Console.WriteLine("Client forcefully disconnected");
                current.Close(); // Dont shutdown because the socket may be disposed and its disconnected anyway
                mClientSockets.Remove(current);
                return;
            }

            //Debug.WriteLine(string.Format("received Length={0}.", received));
            //Debug.WriteLine("Buffer:");
            //Debug.WriteLine(string.Format("{0}.", ToHex(_buffer, 0, received)));

            //for (int i = 0; i < received; i++)
            //    Debug.Write(string.Format("{0,-3}", ToASCIIPrintable(_buffer[i])));
            //Debug.WriteLine(".");

            //for (int i = 0; i < received; i++)
            //    Debug.Write(string.Format("{0,-3}", i.ToString("X")));
            //Debug.WriteLine(".");
            //for (int i = 0; i < received; i++)
            //    Debug.Write(string.Format("{0,-3}", i));
            //Debug.WriteLine(".");

            Debug.WriteLine("_buffer:");
            Debug.WriteLine(DumpBuffer(_buffer, 0, received));

            if (CCommon.ToBytes(CCommon.ToHex(_buffer), received, out gbaMyData))
                Debug.WriteLine("OK");
            else
                Debug.WriteLine("fail");

            Debug.WriteLine("gbaMyData:");
            Debug.WriteLine(DumpBuffer(gbaMyData, 0, received));

            //if (CompareBytes(_buffer, gbaMyData, 0, received) == 0)
            //    Debug.WriteLine("same");
            //else
            //    Debug.WriteLine("diff");
            Debug.Assert(CompareBytes(_buffer, gbaMyData, 0, received) == 0, "diff");
            CCommon.SaveBuffer(@"C:\temp\Buffer-Server-Receive.txt", gbaMyData, 0, received);


            byte[] recBuf = new byte[received];

            Array.Copy(_buffer, recBuf, received);
            string text = Encoding.ASCII.GetString(recBuf);
            Console.WriteLine("Received Text: " + text);

            if (text.ToLower() == "get time") // Client requested time
            {
                Console.WriteLine("Text is a get time request");
                byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
                current.Send(data);
                Console.WriteLine("Time sent to client");
            }
            else if (text.ToLower() == "ping") 
            {
                Console.WriteLine("ping request");
                byte[] data = Encoding.ASCII.GetBytes("pong");
                current.Send(data);
                Console.WriteLine("pong");
            }
            else if (text.ToLower() == "exit") // Client wants to exit gracefully
            {
                // Always Shutdown before closing
                current.Shutdown(SocketShutdown.Both);
                current.Close();
                mClientSockets.Remove(current);
                Console.WriteLine("Client disconnected");
                return;
            }
            else
            {
                Console.WriteLine("Text is an invalid request");
                byte[] data = Encoding.ASCII.GetBytes("Invalid request");
                current.Send(data);
                Console.WriteLine("Warning Sent");
            }

            current.BeginReceive(_buffer, 0, gcBUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
        }

    }
}
